#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <strings.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>     /*包含close函数，关闭套接字文件描述符*/
#include "chat.h"

#define PORTNUMBER      8888

static struct OnLine online[MAX_USER_NUM] = {0};



void user_logout(int index)
{
    int i;
    char buf[BUFFER_MAX_SIZE] = {0};

    if(index < 0 || index >= MAX_USER_NUM)
    {
        return;
    }
    close(online[index].fd);
    /*-1代表该用户已下线*/
    online[index].fd = -1;
    /*通知所有客户端，某个用户下线了*/
    sprintf(buf, "%s offline\n", online[index].name);
    for(i = 0; i<MAX_USER_NUM; i++)
    {
        if(online[i].fd == -1)
        {
            continue;
        }
        send(online[i].fd, buf, strlen(buf), 0);
    }

}

int find_user(char *name)
{
    int i;
    for(i = 0; i < MAX_USER_NUM; i++)
    {
        if(online[i].flag == -1)
        {
            continue;
        }
        if(strncmp(name, online[i].name, 32) == 0)
        {
            return i;
        }
    }
    return -1;
}

int add_user(int sockfd, struct MyProtocol *msg)
{
    int i, index = -1;
    for(i = 0; i < MAX_USER_NUM; i++)
    {
        if(online[i].flag == -1)
        {
            online[i].flag = 1;
            //online[i].fd = sockfd;
            strncpy(online[i].name, msg->name, strlen(msg->name));
            strncpy(online[i].passwd, msg->data, strlen(msg->data));
            online[i].name[NAME_MAX_SIZE-1] = '\0';
            online[i].passwd[PASSWD_MAX_SIZE-1] = '\0';
            index = i;
            return index;
        }
    }
    return index;

}

int user_online(int sockfd, int *index, struct MyProtocol *msg)
{
    int i;
    for(i = 0; i < MAX_USER_NUM; i++)
    {
        if(online[i].flag == -1)
        {
            continue;
        }
        /*如果name和password都匹配的话，说明已经注册过，否则没有注册*/
        if(strncmp(msg->name, online[i].name, strlen(online[i].name)) == 0)
        {
            if(strncmp(msg->data, online[i].passwd, strlen(online[i].passwd)) == 0)
            {
                if(online[i].fd == -1)
                {
                    online[i].fd = sockfd;
                    *index = i;
                    return OP_OK;
                }
                else
                {
                    printf("%s had login!\n", online[i].name);
                    return USER_LOGED;
                }
            }
            else 
            {
                return NAME_PWD_NMATCH;
            }
        }

    }
    return USER_NOT_REGIST;
    
}

void registe(int sockfd, int *index, struct MyProtocol *msg)
{
    int user_index = -1;
    struct MyProtocol msg_back;

    msg_back.cmd = REGISTE;
    /*查询注册名单是否已经注册*/
    user_index = find_user(msg->name);
    if(user_index == -1)
    {
        /*register*/
        *index = add_user(sockfd, msg);
        msg_back.state = OP_OK;

        printf("user %s regist success!\n", msg->name);
        send(sockfd, &msg_back, sizeof(struct MyProtocol), 0);
        return;
    }
    else
    {
        msg_back.state = NAME_EXIST;
        printf("user %s exist!\n", msg->name);
        send(sockfd, &msg_back, sizeof(struct MyProtocol), 0);
        return;
    }
}


void login(int sockfd, int *index, struct MyProtocol *msg)
{
    int i, ret;
    char buf[BUFFER_MAX_SIZE] = {0};
    struct MyProtocol msg_back;

    msg_back.cmd = LOGIN;

    /*用户登录*/
    ret = user_online(sockfd, index, msg);

    if(ret == OP_OK)
    {
        msg_back.state = OP_OK;
        strncpy(msg_back.data, "login success!\n", 128);
        printf("user %s login success! index = %d\n", msg->name, *index);
        send(online[*index].fd, &msg_back, sizeof(struct MyProtocol), 0);
    }
    else
    {
        msg_back.state = ret;
        if(ret == USER_LOGED)
        {
            printf("user %s has been login!\n", msg->name);
        }
        else 
        {
            printf("user %s login fail!\n", msg->name);
        }
        
        send(sockfd, &msg_back, sizeof(struct MyProtocol), 0);
        return;
    }

    /*通知所有客户端，某个用户上线了*/
    sprintf(buf, "%s online\n", msg->name);
    for(i = 0; i<MAX_USER_NUM; i++)
    {
        if(online[i].fd != -1)
        {
            send(online[i].fd, buf, strlen(buf), 0);
        }
    }
}


void broadcast(int index, struct MyProtocol *msg)
{
    int i;
    char buf[BUFFER_MAX_SIZE] = {0};

    sprintf(buf, "%s say: %s\n", online[index].name, msg->data);

    for(i = 0; i < MAX_USER_NUM; i++)
    {
        if((online[i].fd == -1) || (i == index))
        {
            continue;
        }
        send(online[i].fd, buf, strlen(buf), 0);
    }
}

void private(int index, struct MyProtocol *msg)
{
    int dest_index;
    char buf[BUFFER_MAX_SIZE] = {0};

    dest_index = find_user(msg->name);

    if(dest_index == -1)
    {
        sprintf(buf, "there is no user named %s\n", msg->name);
        send(online[index].fd, buf, strlen(buf), 0);
    }
    else
    {
        sprintf(buf, "[%s say to %s]: %s\n", online[index].name, msg->name, msg->data);
        send(online[dest_index].fd, buf, strlen(buf), 0);
    }
}

void list_online_user(int index)
{
    int i;
    struct MyProtocol msg;

    for(i = 0; i < MAX_USER_NUM; i++)
    {
        if(online[i].fd == -1)
        {
            continue;
        }
        memset(&msg, 0, sizeof(msg));
        msg.cmd = ONLINEUSER;
        msg.state = ONLINEUSER_OK;
        strncpy(msg.name, online[i].name, 32);
        printf("[list online user] %d : name = %s\n", i, msg.name);

        send(online[i].fd, &msg, sizeof(msg), 0);
    }
    memset(&msg, 0, sizeof(msg));
    msg.cmd = ONLINEUSER;
    msg.state = ONLINEUSER_OVER;
    send(online[i].fd, &msg, sizeof(msg), 0);

}




/**
 * @brief         线程处理函数，接收用户请求，处理用户请求
 * 
 * @param         arg: socket file descriptor
 * @return        void*
 * @attention
 */
void *rec_func(void *arg)
{
    int new_fd = *((int *)arg);
    int nbytes;

    struct MyProtocol msg;
    int index = -1;

    /*释放在主线程中分配的空间*/
    free(arg);


    /*处理用户信息*/
    while(1)
    {
        nbytes = recv(new_fd, &msg, sizeof(struct MyProtocol), 0);
        
        if(nbytes <= 0)
        {
            /*接收消息出错，将用户下线*/
            if(index == -1)
            {
                close(new_fd);

            }
            else 
            {
                user_logout(index);
            }
            return (void *)-1;
        }

        if(index == -1 && msg.cmd != REGISTE && msg.cmd == LOGIN)
        {
            close(new_fd);
            return (void *)-1;
        }

        /*根据指令的不同，执行不同的操作*/
        switch (msg.cmd)
        {
            case REGISTE:
                registe(new_fd, &index, &msg);
                break;
            case LOGIN:
                login(new_fd, &index, &msg);
                break;
            case BROADCAST:
                broadcast(index, &msg);
                break;
            case PRIVATE:
                private(index, &msg);
                break;
            case ONLINEUSER:
                list_online_user(index);
                break;
            default:
                break;
        }
        
    }

    return (void *)0;
    
}


int main(int argc, char **argv)
{
    int sockfd, new_fd;
    unsigned int sin_size;
    int *pconnsock = NULL;
    int ret;
    pthread_t tid;
    int i=0;

    struct sockaddr_in server_addr;     /*struct sockaddr_in是在头文件<arpa/inet.h>中定义*/
    struct sockaddr_in client_addr;

    /*服务器端开始建立socket描述符  sockfd用于监听*/
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if(sockfd == -1)
    {
        printf("socket establish error\n");
        return -1;
    }

    /*服务器端填充sockaddr结构*/
    bzero(&server_addr, sizeof(struct sockaddr_in));
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = htonl(INADDR_ANY);    /*自动获取网卡地址*/
    server_addr.sin_port = htons(PORTNUMBER);

    /*绑定socket描述符*/
    if(bind(sockfd, (struct sockaddr *)(&server_addr), sizeof(struct sockaddr)) == -1)
    {
        printf("bind error\n");
        return -1;
    }

    /*监听sockfd描述符，一次性监听10个客户端*/
    if(listen(sockfd, 10) == -1)
    {
        printf("listen error\n");
        return -1;
    }
    sin_size = sizeof(struct sockaddr);

    for(i = 0; i < MAX_USER_NUM; i++)
    {
        online[i].fd = -1;
        online[i].flag = -1;
    }

    /*循环接收客户端的连接*/
    while(1)
    {
        /*accept函数的将阻塞的接收客户端的连接*/
        new_fd = accept(sockfd, (struct sockaddr *)(&client_addr), &sin_size);
        if(new_fd == -1)
        {
            printf("accept error\n");
            return -1;
        }

        /*利用malloc函数分配new_fd的位置，防止在thread1处理线程中，
         *未及时取走new_fd的而导致主线程又再一次覆盖了new_fd的值，
         *因为传递给pthread_create函数的参数是指针，每一个线程得到的
         *new_fd地址都是一样的*/
         /*采用堆内存的方式，避免传递同一个地址*/
        pconnsock = (int *)malloc(sizeof(int));
        *pconnsock = new_fd;
        /*为每一个客户端连接创建一个线程进行处理*/
        ret = pthread_create(&tid, NULL, rec_func, (void *)pconnsock);
        if(ret != 0)
        {
            perror("pthread_create");
            return -1;
        }
    }

    close(sockfd);

    return 0;
}















